/**
 * @(#)HttpClientUtil.java, 1月 06, 2021.
 * <p>
 * Copyright 2021 pinghang.com. All rights reserved.
 * PINGHANG.COM PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.tronapi.c.utils;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Consts;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * <h2>http工具类<h2>
 *
 * @author sqq
 * date 2021/1/6
 */
@Slf4j
public class HttpClientUtil {

    private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);

    private static PoolingHttpClientConnectionManager cm;

    private static CloseableHttpClient httpClient;

    private static RequestConfig customRequestConfig;


    static {
        cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(200);
        cm.setDefaultMaxPerRoute(200);
        httpClient = sslHttpClientBuild();
        customRequestConfig = RequestConfig.custom().setSocketTimeout(3000)
                .setConnectTimeout(3000).setConnectionRequestTimeout(3000).build();

    }

    /**
     * 信任所有证书
     *
     * @return
     */
    public static CloseableHttpClient sslHttpClientBuild() {
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", trustAllHttpsCertificates()).build();
        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connectionManager).build();
        return httpClient;
    }

    /**
     * http 同步post请求
     *
     * @param url
     * @param params
     * @return
     */
    public static String syncPost(String url, Map<String, Object> params) {
        String result = null;
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        if (null != params) {
            for (Map.Entry<String, Object> param : params.entrySet()) {
                formparams.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
            }
        }
        UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, Consts.UTF_8);
        final HttpPost httppost = new HttpPost(url);
        httppost.setEntity(entity);
        httppost.setConfig(customRequestConfig);

        CloseableHttpResponse response;
        try {
            response = httpClient.execute(httppost);
            result = EntityUtils.toString(response.getEntity(), "utf-8");
            response.close();
            if (log.isInfoEnabled()) {
                log.info("syncPost url:{},params:{}，result:{}", url, params, result);
            }
        } catch (IOException e) {
            log.error("syncPost failed url:{}, params={}", url, params, e);
        }

        return result;
    }

    /**
     * http 同步post请求
     *
     * @param url
     * @param params
     * @param T
     * @param <T>
     * @return
     */
    public static <T> T syncPost(String url, Map<String, Object> params, Class<T> T) {
        String result = syncPost(url, params);
        if (!T.equals(String.class)) {
            return JSON.parseObject(result, T);
        } else {
            return (T) result;
        }
    }


    /**
     * http 同步get请求
     *
     * @param url
     * @param params
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T syncGet(String url, Map<String, Object> params, Class<T> clazz) {
        String result = syncGet(url, params);
        if (!clazz.equals(String.class)) {
            return JSON.parseObject(result, clazz);
        } else {
            return (T) result;
        }
    }

    /**
     * http 同步get请求
     *
     * @param url    地址
     * @param params 参数
     * @return 结果字符串
     */
    public static String syncGet(String url, Map<String, Object> params) {
        String result = "";
        final HttpGet httpget = new HttpGet(buildHttpGetUrl(url, params));
        httpget.setConfig(customRequestConfig);
        CloseableHttpResponse response;
        try {
            response = httpClient.execute(httpget);
            result = EntityUtils.toString(response.getEntity(), "utf-8");
            response.close();
            if (log.isInfoEnabled()) {
                log.info("syncGet url:{},params:{}，result:{}", url, params, result);
            }
        } catch (IOException e) {
            log.error("syncGet failed url:{}, params={}", url, params, e);
        }
        return result;
    }


    /**
     * 拼装url，参数默认不排序
     *
     * @param baseUrl
     * @param params
     * @return
     */
    public static String buildHttpGetUrl(String baseUrl, Map<String, Object> params) {
        return buildUrl(baseUrl, params, false);
    }

    /**
     * 拼装url
     *
     * @param baseUrl
     * @param params
     * @param sortParam 参数是否排序
     * @return
     */
    public static String buildUrl(String baseUrl, Map<String, Object> params, boolean sortParam) {
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        if (null != params) {
            if (sortParam) {
                params = sortMapByKey(params);
            }
            for (Map.Entry<String, Object> param : params.entrySet()) {
                formparams.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
            }
        }
        return baseUrl + "?" + URLEncodedUtils.format(formparams, Consts.UTF_8);
    }


    public static String buildUrl(String baseUrl, Map<String, String> params) {
        List<NameValuePair> formparams = new ArrayList<NameValuePair>();
        if (null != params) {
            for (Map.Entry<String, String> param : params.entrySet()) {
                formparams.add(new BasicNameValuePair(param.getKey(), param.getValue()));
            }
        }
        return baseUrl + "?" + URLEncodedUtils.format(formparams, Consts.UTF_8);
    }


    /**
     * 信任所有证书
     *
     * @return
     */
    private static SSLConnectionSocketFactory trustAllHttpsCertificates() {
        SSLConnectionSocketFactory socketFactory = null;
        TrustManager[] trustAllCerts = new TrustManager[1];
        TrustManager tm = new miTM();
        trustAllCerts[0] = tm;
        SSLContext sc = null;
        try {
            sc = SSLContext.getInstance("TLS");
            sc.init(null, trustAllCerts, null);
            socketFactory = new SSLConnectionSocketFactory(sc, NoopHostnameVerifier.INSTANCE);
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            logger.error("trustAllHttpsCertificates error", e);
        }
        return socketFactory;
    }

    static class miTM implements TrustManager, X509TrustManager {

        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public void checkServerTrusted(X509Certificate[] certs, String authType) {
            //don't check
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType) {
            //don't check
        }
    }

    /**
     * map排序
     *
     * @param map
     * @return
     */
    public static Map<String, Object> sortMapByKey(Map<String, Object> map) {
        if (map == null || map.isEmpty()) {
            return map;
        }
        Map<String, Object> sortMap = new TreeMap<>(String::compareTo);
        sortMap.putAll(map);
        return sortMap;
    }
}
